#define FASTEST
#define NEW_IS_ATTACKED_BY

using System;
using ChessKit.Common.Primitives;

namespace ChessKit.ChessLogics.Internals
{
	internal struct CompactBoard
	{
		/*  56 57 58 59 60 61 62 63 
		 *  48 49 50 51 52 53 54 55 
		 *  40 41 42 43 44 45 46 47 
		 *  32 33 34 35 36 37 38 39 
		 *  24 25 26 27 28 29 30 31 
		 *  16 17 18 19 20 21 22 23 
		 *   8  9 10 11 12 13 14 15
		 * 	 0  1  2  3  4  5  6  7 */
		public const int A1 = 0*8 + 0;
		public const int A2 = 1*8 + 0;
		public const int A3 = 2*8 + 0;
		public const int A4 = 3*8 + 0;
		public const int A5 = 4*8 + 0;
		public const int A6 = 5*8 + 0;
		public const int A7 = 6*8 + 0;
		public const int A8 = 7*8 + 0;

		public const int H1 = 0*8 + 7;
		public const int H8 = 7*8 + 7;

		public const int E1 = 0*8 + 4;
		public const int E8 = 7*8 + 4;

		public const int C1 = 0*8 + 2;
		public const int C8 = 7*8 + 2;
		public const int G8 = 7*8 + 6;
		public const int G1 = 0*8 + 6;

		public const int D1 = 0*8 + 3;
		public const int B1 = 0*8 + 1;
		public const int B8 = 7*8 + 1;
		public const int D8 = 7*8 + 3;
		public const int F8 = 7*8 + 5;
		public const int F1 = 0*8 + 5;

#if FASTEST
		private const int BytesCount = 64;
#elif SIMPLEST
	private const int BytesCount = 32;
#elif X88
    private const int BytesCount = 128;
#endif

		private readonly byte[] _bytes;

		/// <summary>Deep copy</summary>
		public CompactBoard(CompactBoard source)
			: this()
		{
			_bytes = new byte[BytesCount];
			Buffer.BlockCopy(source._bytes, 0, _bytes, 0, BytesCount);
		}

		/// <summary>Empty board</summary>
		/// <param name="stub">is ignored</param>
		public CompactBoard(int stub)
			: this()
		{
			_bytes = new byte[BytesCount];
		}

		public CompactPiece this[int compactPosition]
		{
			get
			{
#if FASTEST
				return (CompactPiece) _bytes[compactPosition];
#elif SIMPLEST
        switch (compactPosition % 2)
        {
          case 0:
            return (CompactPiece)(_bytes[compactPosition / 2] >> 4);
          case 1:
            return (CompactPiece)(_bytes[compactPosition / 2] & 0xF);
        }
        throw new InvalidOperationException("Cannot get here!");
#elif X88
        return (CompactPiece)_bytes[compactPosition];
#endif
			}
			set
			{
#if FASTEST
				_bytes[compactPosition] = (byte) value;
#elif SIMPLEST
        var i = compactPosition / 2;
        switch (compactPosition % 2)
        {
          case 0:
            _bytes[i] = (byte)((_bytes[i] & 0xF) | (int)value << 4);
            break;
          case 1:
            _bytes[i] = (byte)((_bytes[i] & 0xF0) | (int)value);
            break;
        }
#elif X88
        _bytes[compactPosition] = (byte)value;
#endif
			}
		}

		public override int GetHashCode()
		{
			unchecked
			{
				int res = _bytes[0];
				for (var i = 1; i < BytesCount; i++)
					res = res*257 ^ _bytes[i];
				return res;
			}
		}

		public SlideMoveValidation IsValidRookMove(int from, int to)
		{
			if (Abs(from - to) > 7)
			{
				// vertical move
				if (from%8 != to%8)
				{
					return SlideMoveValidation.PieceDoesntMoveThisWay;
				}

				if (from < to)
				{
					for (var i = from + 8; i < to; i += 8)
						if (this[i] != CompactPiece.EmptyCell)
							return SlideMoveValidation.JumpRequired;
				}
				else
				{
					for (var i = from - 8; i > to; i -= 8)
						if (this[i] != CompactPiece.EmptyCell)
							return SlideMoveValidation.JumpRequired;
				}
				return SlideMoveValidation.Valid;
			}
			// horizontal move
			if (from/8 != to/8)
			{
				return SlideMoveValidation.PieceDoesntMoveThisWay;
			}

			if (from < to)
			{
				for (var i = from + 1; i < to; i++)
					if (this[i] != CompactPiece.EmptyCell)
						return SlideMoveValidation.JumpRequired;
			}
			else
			{
				for (var i = from - 1; i > to; i--)
					if (this[i] != CompactPiece.EmptyCell)
						return SlideMoveValidation.JumpRequired;
			}
			return SlideMoveValidation.Valid;
		}

		public bool IsValidRookMoveFast(int from, int to)
		{
			if (Abs(from - to) > 7)
			{
				if (from%8 != to%8) return false;
				if (from < to)
				{
					for (var i = from + 8; i < to; i += 8)
						if (this[i] != CompactPiece.EmptyCell)
							return false;
				}
				else
				{
					for (var i = from - 8; i > to; i -= 8)
						if (this[i] != CompactPiece.EmptyCell)
							return false;
				}
				return true;
			}
			if (from/8 != to/8) return false;

			if (from < to)
			{
				for (var i = from + 1; i < to; i++)
					if (this[i] != CompactPiece.EmptyCell)
						return false;
			}
			else
			{
				for (var i = from - 1; i > to; i--)
					if (this[i] != CompactPiece.EmptyCell)
						return false;
			}
			return true;
		}

		public bool IsAttackedByFileOrRank(int to, PieceColor color)
		{
			var rook = Piece.Get(PieceType.Rook, color);
			var queen = Piece.Get(PieceType.Queen, color);
			for (var i = to + 8; i < 64; i += 8)
			{
				var piece = this[i];
				if (piece != CompactPiece.EmptyCell)
					if (piece == rook || piece == queen)
						return true;
					else break;
			}
			for (var i = to - 8; i >= 0; i -= 8)
			{
				var piece = this[i];
				if (piece != CompactPiece.EmptyCell)
					if (piece == rook || piece == queen)
						return true;
					else break;
			}
			var limit = (to/8 + 1)*8;
			for (var i = to + 1; i < limit; i++)
			{
				var piece = this[i];
				if (piece != CompactPiece.EmptyCell)
					if (piece == rook || piece == queen)
						return true;
					else break;
			}
			limit = (to/8)*8;
			for (var i = to - 1; i >= limit; i--)
			{
				var piece = this[i];
				if (piece != CompactPiece.EmptyCell)
					if (piece == rook || piece == queen)
						return true;
					else break;
			}
			return false;
		}

		public SlideMoveValidation IsValidBishopMove(int from, int to)
		{
			var dist = from - to;
			if (dist%9 == 0)
			{
				if (from < to)
				{
					if (from%8 >= to%8 || from/8 >= to/8)
						return SlideMoveValidation.PieceDoesntMoveThisWay;

					for (var i = from + 9; i < to; i += 9)
						if (this[i] != CompactPiece.EmptyCell)
							return SlideMoveValidation.JumpRequired;
				}
				else
				{
					if (from%8 <= to%8 || from/8 <= to/8)
						return SlideMoveValidation.PieceDoesntMoveThisWay;

					for (var i = from - 9; i > to; i -= 9)
						if (this[i] != CompactPiece.EmptyCell)
							return SlideMoveValidation.JumpRequired;
				}

				return SlideMoveValidation.Valid;
			}
			if (dist%7 == 0)
			{
				if (from < to)
				{
					if (from%8 <= to%8 || from/8 >= to/8)
						return SlideMoveValidation.PieceDoesntMoveThisWay;

					for (var i = from + 7; i < to; i += 7)
						if (this[i] != CompactPiece.EmptyCell)
							return SlideMoveValidation.JumpRequired;
				}
				else
				{
					if (from%8 >= to%8 || from/8 <= to/8)
						return SlideMoveValidation.PieceDoesntMoveThisWay;

					for (var i = from - 7; i > to; i -= 7)
						if (this[i] != CompactPiece.EmptyCell)
							return SlideMoveValidation.JumpRequired;
				}

				return SlideMoveValidation.Valid;
			}
			return SlideMoveValidation.PieceDoesntMoveThisWay;
		}

		public bool IsValidBishopMoveFast(int from, int to)
		{
			var dist = from - to;
			if (dist%9 == 0)
			{
				if (from < to)
				{
					if (from%8 >= to%8 || from/8 >= to/8)
						return false;

					for (var i = from + 9; i < to; i += 9)
						if (this[i] != CompactPiece.EmptyCell)
							return false;
				}
				else
				{
					if (from%8 <= to%8 || from/8 <= to/8)
						return false;

					for (var i = from - 9; i > to; i -= 9)
						if (this[i] != CompactPiece.EmptyCell)
							return false;
				}

				return true;
			}
			if (dist%7 == 0)
			{
				if (from < to)
				{
					if (from%8 <= to%8 || from/8 >= to/8)
						return false;

					for (var i = from + 7; i < to; i += 7)
						if (this[i] != CompactPiece.EmptyCell)
							return false;
				}
				else
				{
					if (from%8 >= to%8 || from/8 <= to/8)
						return false;

					for (var i = from - 7; i > to; i -= 7)
						if (this[i] != CompactPiece.EmptyCell)
							return false;
				}

				return true;
			}
			return false;
		}

		public bool IsAttackedByDiagonal(int to, PieceColor color)
		{
			var bishop = Piece.Get(PieceType.Bishop, color);
			var queen = Piece.Get(PieceType.Queen, color);
			var limit = Math.Min(to + (7 - to%8)*9, 63);
			for (var i = to + 9; i <= limit; i += 9)
			{
				var piece = this[i];
				if (piece != CompactPiece.EmptyCell)
					if (piece == bishop || piece == queen)
						return true;
					else break;
			}
			limit = Math.Max(to - (to%8)*9, 0);
			for (var i = to - 9; i >= limit; i -= 9)
			{
				var piece = this[i];
				if (piece != CompactPiece.EmptyCell)
					if (piece == bishop || piece == queen)
						return true;
					else break;
			}
			limit = Math.Min(to + to%8*7, 63);
			for (var i = to + 7; i <= limit; i += 7)
			{
				var piece = this[i];
				if (piece != CompactPiece.EmptyCell)
					if (piece == bishop || piece == queen)
						return true;
					else break;
			}
			limit = Math.Max(to - (7 - to%8)*7, 0);
			for (var i = to - 7; i >= limit; i -= 7)
			{
				var piece = this[i];
				if (piece != CompactPiece.EmptyCell)
					if (piece == bishop || piece == queen)
						return true;
					else break;
			}
			return false;
		}

		public bool IsValidKnightMove(int from, int to)
		{
			switch (from - to)
			{
				case -17:
					return from%8 < to%8 && from/8 < to/8;
				case -15:
					return from%8 > to%8 && from/8 < to/8;
				case -10:
					return from%8 < to%8 && from/8 < to/8;
				case -06:
					return from/8 < to/8;
				case +17:
					return from%8 > to%8 && from/8 > to/8;
				case +10:
					return from%8 > to%8 && from/8 > to/8;
				case +15:
					return from%8 < to%8 && from/8 > to/8;
				case +06:
					return from/8 > to/8;
				default:
					return false;
			}
		}

		public bool IsAttackedByKnight(int to, PieceColor color)
		{
			var exp = Piece.Get(PieceType.Knight, color); // exp - expected piece
			var f = to%8; // f - file
			var r = to/8; // r - rank
			var // s - square
				s = to - 17;
			if (s%8 < f && s/8 < r && s >= 0 && this[s] == exp) return true;
			s = to - 15;
			if (s%8 > f && s/8 < r && s >= 0 && this[s] == exp) return true;
			s = to - 10;
			if (s%8 < f && s/8 < r && s >= 0 && this[s] == exp) return true;
			s = to - 06;
			if (s/8 < r && s >= 0 && this[s] == exp) return true;
			s = to + 17;
			if (s%8 > f && s/8 > r && s < 64 && this[s] == exp) return true;
			s = to + 10;
			if (s%8 > f && s/8 > r && s < 64 && this[s] == exp) return true;
			s = to + 15;
			if (s%8 < f && s/8 > r && s < 64 && this[s] == exp) return true;
			s = to + 06;
			if (s/8 > r && s < 64 && this[s] == exp) return true;
			return false;
		}

		public KingMoveValidation IsValidKingCastlingMove(int fromSquare, int to)
		{
			var color = Piece.Color(this[fromSquare]).Invert();
			switch (fromSquare - to)
			{
				case +2:
					if (IsUnderAttack(color, fromSquare))
						return KingMoveValidation.CantCastleUnderAttack;
					if (fromSquare == E1)
					{
						if (this[D1] != 0 || this[B1] != 0)
							return KingMoveValidation.DoesntMoveThisWay;
						return IsUnderAttack(color, D1)
							       ? KingMoveValidation.CantCastleThroughCellUnderAttack
                     : KingMoveValidation.Valid;
					}
					if (fromSquare == E8)
					{
						if (this[D8] != 0 || this[B8] != 0)
							return KingMoveValidation.DoesntMoveThisWay;
						return IsUnderAttack(color, D8)
							       ? KingMoveValidation.CantCastleThroughCellUnderAttack
                     : KingMoveValidation.Valid;
					}
					return KingMoveValidation.DoesntMoveThisWay;
				case -2:
					if (IsUnderAttack(color, fromSquare))
						return KingMoveValidation.CantCastleUnderAttack;
					if (fromSquare == E1)
					{
						if (this[F1] != 0) return KingMoveValidation.DoesntMoveThisWay;
						return IsUnderAttack(color, F1)
							       ? KingMoveValidation.CantCastleThroughCellUnderAttack
                     : KingMoveValidation.Valid;
					}
					if (fromSquare == E8)
					{
						if (this[F8] != 0) return KingMoveValidation.DoesntMoveThisWay;
						return IsUnderAttack(color, F8)
							       ? KingMoveValidation.CantCastleThroughCellUnderAttack
                     : KingMoveValidation.Valid;
					}
					return KingMoveValidation.DoesntMoveThisWay;
				default:
					return KingMoveValidation.DoesntMoveThisWay;
			}
		}

		public bool IsValidKingMove(int fromSquare, int to)
		{
			switch (fromSquare - to)
			{
				case -7:
				case +9:
					return fromSquare%8 > to%8;
				case -1:
				case +1:
					return fromSquare/8 == to/8;
				case -9:
				case +7:
					return fromSquare%8 < to%8;
				case -8:
				case +8:
					return true;
				default:
					return false;
			}
		}

		public bool CanBeValidKingMove(int fromSquare, int to)
		{
			switch (fromSquare - to)
			{
				case -7:
				case +9:
					return fromSquare%8 > to%8;
				case -1:
				case +1:
					return fromSquare/8 == to/8;
				case -9:
				case +7:
					return fromSquare%8 < to%8;
				case -8:
				case +8:
					return true;
				case +2:
				case -2:
					return fromSquare == E1 || fromSquare == E8;
				default:
					return false;
			}
		}

		public bool IsAttackedByKing(int to, PieceColor color)
		{
			var exp = Piece.Get(PieceType.King, color); // exp - expected piece
			var // s - square
				s = to - 8;
			if (s >= 0 && this[s] == exp) return true;
			s = to + 8;
			if (s < 64 && this[s] == exp) return true;
			var r = to%8; // r - rank
			s = to - 7;
			if (s%8 > r && s >= 0 && this[s] == exp) return true;
			s = to + 9;
			if (s%8 > r && s < 64 && this[s] == exp) return true;
			s = to - 9;
			if (s%8 < r && s >= 0 && this[s] == exp) return true;
			s = to + 7;
			if (s%8 < r && s < 64 && this[s] == exp) return true;
			var f = to/8; // f - file
			s = to - 1;
			if (s/8 == f && s >= 0 && this[s] == exp) return true;
			s = to + 1;
			if (s/8 == f && s < 64 && this[s] == exp) return true;
			return false;
		}

		public bool PawnAttacks(PieceColor color, int f, int to)
		{
			return color == PieceColor.White
				       ? (to - f == 9 || to - f == 7) && to/8 == f/8 + 1
				       : (f - to == 9 || f - to == 7) && to/8 == f/8 - 1;
		}

		public bool IsAttackedByPawn(int to, PieceColor color)
		{
			var exp = Piece.Get(PieceType.Pawn, color); // exp - expected piece
			int s; // s - square
			var f = to/8; // f - file
			if (color == PieceColor.White)
			{
				s = to - 9;
				if (f == s/8 + 1 && s >= 0 && this[s] == exp) return true;
				s = to - 7;
				if (f == s/8 + 1 && s >= 0 && this[s] == exp) return true;
			}
			else
			{
				s = to + 9;
				if (f == s/8 - 1 && s < 64 && this[s] == exp) return true;
				s = to + 7;
				if (f == s/8 - 1 && s < 64 && this[s] == exp) return true;
			}
			return false;
		}

		public PawnMoveValidation IsValidPawnMove(PieceColor color, int from, int to)
		{
			if (color == PieceColor.White)
				switch (to - from)
				{
					case 16:
						if (from/8 != 1) return PawnMoveValidation.DoesntMoveThisWay;
						if (this[to] != CompactPiece.EmptyCell)
							return PawnMoveValidation.DoesntTakeThisWay;
						return this[from + 8] != CompactPiece.EmptyCell
							       ? PawnMoveValidation.JumpRequired
							       : PawnMoveValidation.DoubleMove;
					case 8:
						if (this[to] != CompactPiece.EmptyCell)
							return PawnMoveValidation.DoesntTakeThisWay;
						return to/8 == 7
							       ? PawnMoveValidation.Promotion
							       : PawnMoveValidation.Move;
					case 9:
					case 7:
						if (to/8 != from/8 + 1) return PawnMoveValidation.DoesntMoveThisWay;
						if (this[to] == CompactPiece.EmptyCell)
							return to/8 == 5
								       ? PawnMoveValidation.EnPassant
								       : PawnMoveValidation.OnlyTakesThisWay;
						return to/8 != 7
							       ? PawnMoveValidation.Take
							       : PawnMoveValidation.Promotion | PawnMoveValidation.Take;
					default:
						return PawnMoveValidation.DoesntMoveThisWay;
				}
			switch (from - to)
			{
				case 16:
					if (from/8 != 6) return PawnMoveValidation.DoesntMoveThisWay;
					if (this[to] != CompactPiece.EmptyCell)
						return PawnMoveValidation.DoesntTakeThisWay;
					return this[from - 8] != CompactPiece.EmptyCell
						       ? PawnMoveValidation.JumpRequired
						       : PawnMoveValidation.DoubleMove;
				case 8:
					if (this[to] != CompactPiece.EmptyCell)
						return PawnMoveValidation.DoesntTakeThisWay;
					return to/8 == 0
						       ? PawnMoveValidation.Promotion
						       : PawnMoveValidation.Move;
				case 9:
				case 7:
					if (to/8 != from/8 - 1) return PawnMoveValidation.DoesntMoveThisWay;
					if (this[to] == CompactPiece.EmptyCell)
						return to/8 == 2
							       ? PawnMoveValidation.EnPassant
							       : PawnMoveValidation.OnlyTakesThisWay;
					return to/8 != 0
						       ? PawnMoveValidation.Take
						       : PawnMoveValidation.Promotion | PawnMoveValidation.Take;
				default:
					return PawnMoveValidation.DoesntMoveThisWay;
			}
		}

		public bool CanBeValidPawnMoveFast(PieceColor color, int fromSquare, int to)
		{
			// Do not check if en-passant is allowed
			if (color == PieceColor.White)
			{
				switch (to - fromSquare)
				{
					case 16:
						if (fromSquare/8 != 1) return false;
						if (this[to] != CompactPiece.EmptyCell) return false;
						return this[fromSquare + 8] == CompactPiece.EmptyCell;
					case 8:
						return this[to] == CompactPiece.EmptyCell;
					case 9:
					case 7:
						if (to/8 != fromSquare/8 + 1) return false;
						return this[to] != CompactPiece.EmptyCell || to/8 == 5;
				}
			}
			else
			{
				switch (fromSquare - to)
				{
					case 16:
						if (fromSquare/8 != 6) return false;
						if (this[to] != CompactPiece.EmptyCell) return false;
						return this[fromSquare - 8] == CompactPiece.EmptyCell;
					case 8:
						return this[to] == CompactPiece.EmptyCell;
					case 9:
					case 7:
						if (to/8 != fromSquare/8 - 1) return false;
						return this[to] != CompactPiece.EmptyCell || to/8 == 2;
				}
			}
			return false;
		}

		public static int Abs(int value)
		{
			return value < 0 ? -value : value;
		}

		public bool IsUnderAttack(int from, int cell, CompactPiece fromPiece)
		{
			switch (Piece.Type(fromPiece))
			{
				case PieceType.King:
					return IsValidKingMove(from, cell);
				case PieceType.Bishop:
					return IsValidBishopMove(from, cell) == SlideMoveValidation.Valid;
				case PieceType.Rook:
					return IsValidRookMove(from, cell) == SlideMoveValidation.Valid;
				case PieceType.Pawn:
					return PawnAttacks(Piece.Color(fromPiece), from, cell);
				case PieceType.Knight:
					return IsValidKnightMove(from, cell);
				case PieceType.Queen:
					return IsValidBishopMove(from, cell) == SlideMoveValidation.Valid
					       || IsValidRookMove(from, cell) == SlideMoveValidation.Valid;
				default:
					return false;
			}
		}

		public bool IsUnderAttack(PieceColor color, int cell)
		{
#if NEW_IS_ATTACKED_BY
			if (IsAttackedByDiagonal(cell, color)) return true;
			if (IsAttackedByKing(cell, color)) return true;
			if (IsAttackedByFileOrRank(cell, color)) return true;
			if (IsAttackedByKnight(cell, color)) return true;
			if (IsAttackedByPawn(cell, color)) return true;
			return false;
#else
      for (var from = 0; from < 64; from++)
      {
        var fromPiece = this[from];
        if (Piece.Color(fromPiece) != color) continue;
        if (IsUnderAttack(from, cell, fromPiece)) return true;
      }
      return false;
#endif
		}

		public int FindKing(PieceColor color)
		{
			var king = Piece.Get(PieceType.King, color);
			for (int i = 0; i < 64; i++)
				if (this[i] == king)
					return i;
			return -1;
		}

		public bool IsUnderCheck(PieceColor kingColor)
		{
			var king = FindKing(kingColor);
			return king != -1 && IsUnderAttack(kingColor.Invert(), king);
		}

		public bool CanMoveBeValidFast(PieceType piece, int moveFrom, int moveTo, PieceColor color)
		{
			switch (piece)
			{
				case PieceType.Pawn:
					return CanBeValidPawnMoveFast(color, moveFrom, moveTo);
				case PieceType.Bishop:
					return IsValidBishopMoveFast(moveFrom, moveTo);
				case PieceType.Knight:
					return IsValidKnightMove(moveFrom, moveTo);
				case PieceType.Rook:
					return IsValidRookMoveFast(moveFrom, moveTo);
				case PieceType.Queen:
					return IsValidBishopMoveFast(moveFrom, moveTo)
				        || IsValidRookMoveFast(moveFrom, moveTo);
				case PieceType.King:
					return CanBeValidKingMove(moveFrom, moveTo);
			}
			return false;
		}
	}
}